package vm.baseNativeUseFunction;

import NextClass.NativeClass;
import app.boot.BaseFunction;
import build.Bin.Var;
import expression.BaseExpression;
import expression.BoolExpression;
import expression.MathExpression;
import expression.StringExpression;
import vm.runtime.NextVMRuntime;
import vm.runtime.TypeParse;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;

import static app.boot.BaseFunction.getAllFilePaths;

public class NativeUseFunction {
    public static String[] NativeMethodList = {
            "system_time",
            "bool",
            "int",
            "long",
            "float",
            "double",
            "get_os",
            "get_login",
            "get_arch",
            "get_dir",
            "rm",
            "input",
            "str_to_bytes",
            "bytes_to_str",
            "get_file_bytes",
            "substr",
            "find",
            "lfind",
            "get_file_content",
            "get_script_dir",
            "len",
            "split",
            "list_rm",
            "list_add",
            "make_string_png",
            "random",
            "has",
            "true",
            "or_true",
            "list_dir",
            "file_as_obj",
            "obj_to_file",
            "list_index"
    };
    public static boolean IsNativeUseFunctionCode(String code) {
        try {
            String function = code.substring(0,code.indexOf("(")).trim();
            String content = code.substring(code.indexOf("(")+1 , code.lastIndexOf(")")).trim();
            return new ArrayList<String>(Arrays.asList(NativeMethodList)).contains(function);
        }catch (Exception e) {
            return false;
        }
    }
    public static boolean IsNativeUseFunction(String Name) {
        try {
            return new ArrayList<String>(Arrays.asList(NativeMethodList)).contains(Name);
        }catch (Exception e) {
            return false;
        }
    }
    public static Object run_code(String code , NextVMRuntime nextVMRuntime) throws Exception {
        String function = code.substring(0,code.indexOf("(")).trim();
        String content = code.substring(code.indexOf("(")+1 , code.lastIndexOf(")")).trim();
        //String ContentRegex = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)(?=([^\\(]*\\([^\\)]*\\))*[^\\)]*$)";
        String[] tokens = StringExpression.splitStringTokens(content);
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(function);
        arrayList.addAll(Arrays.asList(tokens));
        //System.out.println(Arrays.toString(tokens) +" "+content);
        return run(arrayList.toArray(new String[0]), nextVMRuntime , code);
    }
    public static Object run(String[] tokens , NextVMRuntime nextVMRuntime, String SourceCode)
        throws Exception {
        String Name = tokens[0];
        if (Name.equals("system_time")) {
            return System.currentTimeMillis();
        }
        else if (Name.equals("list_index")) {
            ArrayList<Object> obj_1 = (ArrayList<Object>) BaseExpression.Deal(new String[]{tokens[1]},nextVMRuntime)[0];
            return obj_1.indexOf(BaseExpression.Deal(new String[]{tokens[2]} , nextVMRuntime));
        }
        else if (Name.equals("file_as_obj")) {
            String path = StringExpression.getExpression(tokens[1],nextVMRuntime);
            return FileToObjectConverter.fileAsObject(path);
        }
        else if (Name.equals("obj_to_file")) {
            String path = StringExpression.getExpression(tokens[1],nextVMRuntime);
            Object obj = BaseExpression.Deal(new String[]{tokens[2]},nextVMRuntime)[0];
            if (obj instanceof NativeClass) {
                NativeClass o = (NativeClass) obj;
                obj = o.InitObject;
            }
            FileToObjectConverter.objectToFile(path , obj);
            return null;
        }
        else if (Name.equals("has")) {
            String var1 = StringExpression.getExpression(tokens[1],nextVMRuntime);
            String var2 = StringExpression.getExpression(tokens[2],nextVMRuntime);
            return var1.contains(var2);
        }
        else if (Name.equals("list_dir")) {
            File[] files = new File(StringExpression.getExpression(tokens[1],nextVMRuntime)).listFiles();
            ArrayList<String> arrayList = new ArrayList<>();
            assert files != null;
            for (File f : files) {
                arrayList.add(f.getName());
            }
            return arrayList;
        }
        else if (Name.equals("or_true")) {
            String[] In = Var.joinStringsIndexOf(tokens , 1);
            Object[] objects = BaseExpression.Deal(In, nextVMRuntime);
            boolean ok = false;
            for (Object i : objects) {
                //System.out.println(i);
                boolean b = Boolean.parseBoolean(String.valueOf(i));
                if (b) {
                    ok = true;
                    break;
                }
            }
            return ok;
        }
        else if (Name.equals("true")) {
            String[] In = Var.joinStringsIndexOf(tokens , 1);
            Object[] objects = BaseExpression.Deal(In, nextVMRuntime);
            boolean ok = true;
            for (Object i : objects) {
                //System.out.println(i);
                boolean b = Boolean.parseBoolean(String.valueOf(i));
                if (!b) {
                    ok = false;
                    break;
                }
            }
            return ok;
        }
        else if (Name.equals("bool")) {
            return BoolExpression.getBool(tokens[1] , nextVMRuntime);
        }else if (Name.equals("int")) {
            return (int) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("float")) {
            return (float) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("double")) {
            return (double) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("make_string_png")) {
            String[] ss = {tokens[1] , tokens[2]};
            Object[] objects = BaseExpression.Deal(ss , nextVMRuntime);
            BaseFunction.StringToPng(String.valueOf(objects[0]),String.valueOf(objects[1]));
            return null;
        }
        else if (Name.equals("len")) {
            Object obj = StringExpression.getValueFromNextVM(tokens[1].trim(),nextVMRuntime);
            if (TypeParse.getType(obj).contains("[]")) {
                ArrayList<?> arrayList = (ArrayList<?>) obj;
                assert arrayList != null;
                return arrayList.size();
            }
            return StringExpression.getExpression(tokens[1],nextVMRuntime).length();
        }
        else if (Name.equals("long")) {
            return (long) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }else if (Name.equals("get_os")) {
            return System.getProperty("os.name");
        }else if (Name.equals("get_login")) {
            return System.getProperty("user.name");
        }else if (Name.equals("get_dir")) {
            return System.getProperty("user.dir");
        }else if (Name.equals("get_arch")) {
            return System.getProperty("os.arch");
        }
        else if (Name.equals("rm")) {
            File file = new File(StringExpression.getExpression(tokens[1],nextVMRuntime));
            if (file.isFile()) {
                return file.delete();
            } else {
                List<String> paths = getAllFilePaths(file.getAbsolutePath());
                for (String i : paths) {
                    System.out.println(i);
                    boolean ok = new File(i).delete();
                    if (!ok) {
                        return false;
                    }
                }
                return true;
            }
        }
        else if (Name.equals("get_file_bytes")) {
            File file = new File(StringExpression.getExpression(tokens[1],nextVMRuntime));
            return NextBytes.readFileToByteArray(file.getAbsolutePath());
        }
        else if (Name.equals("random")) {
            int begin = (int) MathExpression.evaluateExpression(tokens[1] , nextVMRuntime);
            int end = (int) MathExpression.evaluateExpression(tokens[2] , nextVMRuntime);
            return BaseFunction.generateRandomNumber(begin , end);
        }
        else if (Name.equals("bytes_to_str")) {
            //System.out.println(Arrays.toString(tokens));
            byte[] bytes = (byte[]) StringExpression.getValueFromNextVM(tokens[1].trim(),nextVMRuntime);
            return new String(bytes, Charset.forName(StringExpression.getExpression(tokens[2],nextVMRuntime)));
        }
        else if (Name.equals("str_to_bytes")) {
            return StringExpression.getExpression(tokens[1],nextVMRuntime).getBytes();
        }
        else if (Name.equals("substr")) {
            String str = String.valueOf(BaseExpression.Deal(new String[]{tokens[1]} , nextVMRuntime)[0]);
            int start = (int) MathExpression.evaluateExpression(
                    String.valueOf(BaseExpression.Deal(new String[]{tokens[2]} , nextVMRuntime)[0]), nextVMRuntime);
            int end = (int) MathExpression.evaluateExpression(
                    String.valueOf(BaseExpression.Deal(new String[]{tokens[3]} , nextVMRuntime)[0]), nextVMRuntime);
            return str.substring(start , end);
        }
        else if (Name.equals("find")) {
            //System.out.println(Arrays.toString(tokens));
            String str = StringExpression.getExpression(tokens[1],nextVMRuntime);
            String findIndex = String.valueOf(BaseExpression.Deal(new String[]{tokens[2]} , nextVMRuntime)[0]);
            return str.indexOf(findIndex);
        }
        else if (Name.equals("lfind")) {
            String str = StringExpression.getExpression(tokens[1],nextVMRuntime);
            String findIndex = String.valueOf(BaseExpression.Deal(new String[]{tokens[2]} , nextVMRuntime)[0]);
            return str.lastIndexOf(findIndex);
        }

        else if (Name.equals("get_file_content") ) {
            String name = StringExpression.getExpression(tokens[1],nextVMRuntime);
            return BaseFunction.getFileContent(name);
        }

        else if (Name.equals("get_script_dir"))
        {
            return nextVMRuntime.SourceFile.getParentFile().getAbsolutePath();
        }

        else if (Name.equals("split")) {
            //System.out.println(Arrays.toString(tokens));
            String[] strings = StringExpression.getExpression(
                    tokens[1],nextVMRuntime).split(
                            StringExpression.getExpression(tokens[2],nextVMRuntime));
            return new ArrayList<String>(Arrays.asList(strings));
        }
        else if (Name.equals("list_add")) {
            return list_add(tokens[1],tokens[2],nextVMRuntime);
        }
        else if (Name.equals("list_rm")) {
            if (StringExpression.IsNumber(tokens[2].trim())) {
                return list_rm_with_index(tokens[1],Integer.parseInt(tokens[2].trim()),nextVMRuntime);
            }
            else {
                return list_rm(tokens[1],tokens[2],nextVMRuntime);
            }
        }
        else {
            return null;
        }
    }
    public static ArrayList<Object> list_add(String list,String add,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.add(StringExpression.getValueFromNextVM(add.trim(),nextVMRuntime));
            nextVMRuntime.values.put(list.trim() , obj_1);
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_add(list,add,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }

    public static ArrayList<Object> list_rm(String list,String add,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.remove(StringExpression.getValueFromNextVM(add.trim(),nextVMRuntime));
            nextVMRuntime.values.put(list.trim() , obj_1);
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_rm(list,add,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }
    public static ArrayList<Object> list_rm_with_index(String list,int index,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.remove(index);
            nextVMRuntime.values.put(list.trim() , obj_1);
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_rm_with_index(list,index,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }
}
